package gqz.testdemo.BtBleDemo;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
import android.support.annotation.RequiresApi;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;

import gqz.testdemo.R;

/**
 * Created by Administrator on 2018/9/18.
 */

public class BleServerDemo extends Activity {

    private String TAG = "BleServerDemo-->";
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothManager mBluetoothManager;
    private BluetoothGattServer mBluetoothGattServer;
    private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
    private RecyclerView msgsView;
    private MsgAdapter mMessageAdapter;
    private SimpleDateFormat mDateFormat;

    private Set<BluetoothDevice> mRegisteredDevices = new HashSet<>();

    //先定几个服务类型的UUID
     /* Current Time Service UUID */
    public static UUID TIME_SERVICE = UUID.fromString("00001805-0000-1000-8000-00805f9b34fb");
    /* Mandatory Current Time Information Characteristic */
    public static UUID CURRENT_TIME = UUID.fromString("00002a2b-0000-1000-8000-00805f9b34fb");
    /* Optional Local Time Information Characteristic */
    public static UUID LOCAL_TIME_INFO = UUID.fromString("00002a0f-0000-1000-8000-00805f9b34fb");
    /* Mandatory Client Characteristic Config Descriptor */
    public static UUID CLIENT_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    mMessageAdapter.addMessage(mDateFormat.format(System.currentTimeMillis()) + "  " + msg.getData().getString("msg"));
                    break;
            }
            return false;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ble_server);
        mDateFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
        init();
        openAndInitBt();//初始化需要一定时间
//        createGattServer();//所以以下这两个方法在这里直接运行是错误的，一定要在蓝牙正确开启，并且支持BLE在执行
//        startAdvertising();//我写在这里只是为了展示调用顺序。切记切记！！
    }

    private void init() {
        mMessageAdapter = new MsgAdapter(this);
        msgsView = (RecyclerView) findViewById(R.id.server_msglist);
        msgsView.setLayoutManager(new LinearLayoutManager(this));
        msgsView.setAdapter(mMessageAdapter);

        IntentFilter i = new IntentFilter();
        i.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        i.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
        i.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        i.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        i.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
        i.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        registerReceiver(receiver, i);
    }

    //1.初始化并打开蓝牙
    private void openAndInitBt() {
        mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            return;
        }//不支持蓝牙
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            outputLog("不支持ble");
            return;//不支持ble蓝牙
        }
        //.判断蓝牙是否打开
        if (!mBluetoothAdapter.enable()) {
            //没打开请求打开
            Intent btEnable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(btEnable, 100);
            outputLog("正在打开蓝牙");
        } else if (mBluetoothAdapter.isEnabled()) {
            createGattServer();//所以以下这两个方法在这里直接运行是错误的，一定要在蓝牙正确开启，并且支持BLE在执行
            startAdvertising();//我写在这里只是为了展示调用顺序。切记切记！！
        }
    }

    //2.创建GATT服务
    private void createGattServer() {
        //2.1.新建一个服务
        BluetoothGattService service = new BluetoothGattService(TIME_SERVICE, BluetoothGattService.SERVICE_TYPE_PRIMARY);
        //2.2 新建一个Characteristic
        BluetoothGattCharacteristic currentTime = new BluetoothGattCharacteristic(CURRENT_TIME,
                //Read-only characteristic, supports notifications
                BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
                BluetoothGattCharacteristic.PERMISSION_READ);
        //2.3 新建特性描述并配置--这一步非必需
        BluetoothGattDescriptor configDescriptor = new BluetoothGattDescriptor(CLIENT_CONFIG,
                //Read/write descriptor
                BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
        currentTime.addDescriptor(configDescriptor);
        //2.4 将特性配置到服务
        service.addCharacteristic(currentTime);
        //2.5 打开外围设备  注意这个services和server的区别，别记错了
        mBluetoothGattServer = mBluetoothManager.openGattServer(this, mGattServerCallback);
        if (mBluetoothGattServer == null) {
            return;
        }
        mBluetoothGattServer.addService(service);
        outputLog("gatt服务创建成功");
    }

    //3.通知服务开启
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void startAdvertising() {
        mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
        if (mBluetoothLeAdvertiser == null) {
            //创建失败
            outputLog("通知服务开启失败");
            return;
        }
        AdvertiseSettings settings = new AdvertiseSettings.Builder()
                .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
                .setConnectable(true)
                .setTimeout(0)
                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
                .build();

        AdvertiseData data = new AdvertiseData.Builder()
                .setIncludeDeviceName(true)
                .setIncludeTxPowerLevel(false)
                .addServiceUuid(new ParcelUuid(TIME_SERVICE))//绑定服务uuid
                .build();
        mBluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback);
        outputLog("开启通知服务");
    }

    private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            Log.i("", "LE Advertise Started.");
            outputLog("开启通知服务成功");
            updateCharacteristic();
        }

        @Override
        public void onStartFailure(int errorCode) {
            Log.w("", "LE Advertise Failed: " + errorCode);
            outputLog("开启通知服务失败");
        }
    };


    /**
     * Callback to handle incoming requests to the GATT server.
     * 所有characteristics 和 descriptors 的读写请求都在这里处理
     * 这里我忽略了处理逻辑，这个根据实际需求写
     */
    private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {

        @Override
        public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
            //连接状态改变
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.i("", "BluetoothDevice CONNECTED: " + device);
                mRegisteredDevices.add(device);
                outputLog("有设备连接到本机：name: " + device.getName() + " addr: " + device.getAddress());
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.i("", "BluetoothDevice DISCONNECTED: " + device);
                mRegisteredDevices.remove(device);
                outputLog("有设备断开连接：name: " + device.getName() + " addr: " + device.getAddress());
            }
        }

        @Override
        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
                                                BluetoothGattCharacteristic characteristic) {
            //请求读特征 如果包含有多个服务，就要区分请求读的是什么，这里我只有一个服务
            if (CURRENT_TIME.equals(characteristic.getUuid())) {
                //回应
                mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, "123456789|123456789|".getBytes());
            }

        }

        @Override
        public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset,
                                            BluetoothGattDescriptor descriptor) {
            if (CLIENT_CONFIG.equals(descriptor.getUuid())) {
                Log.d("", "Config descriptor read");
                byte[] returnValue;
                if (mRegisteredDevices.contains(device)) {
                    returnValue = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
                } else {
                    returnValue = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
                }
                mBluetoothGattServer.sendResponse(device,
                        requestId,
                        BluetoothGatt.GATT_SUCCESS,
                        0,
                        returnValue);
            } else {
                Log.w("", "Unknown descriptor read request");
                mBluetoothGattServer.sendResponse(device,
                        requestId,
                        BluetoothGatt.GATT_FAILURE,
                        0,
                        null);
            }

        }

        @Override
        public void onMtuChanged(BluetoothDevice device, int mtu) {
            super.onMtuChanged(device, mtu);
            outputLog("requestDev: " + device.getName() + " mtu:" + mtu);
        }

        @Override
        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded,
                                             int offset, byte[] value) {
            if (CLIENT_CONFIG.equals(descriptor.getUuid())) {
                if (Arrays.equals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE, value)) {
                    Log.d("", "Subscribe device to notifications: " + device);
                    mRegisteredDevices.add(device);
                } else if (Arrays.equals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE, value)) {
                    Log.d("", "Unsubscribe device from notifications: " + device);
                    mRegisteredDevices.remove(device);
                }

                if (responseNeeded) {
                    mBluetoothGattServer.sendResponse(device,
                            requestId,
                            BluetoothGatt.GATT_SUCCESS,
                            0,
                            null);
                }
            } else {
                Log.w("", "Unknown descriptor write request");
                if (responseNeeded) {
                    mBluetoothGattServer.sendResponse(device,
                            requestId,
                            BluetoothGatt.GATT_FAILURE,
                            0,
                            null);
                }
            }
        }

        //这个实际可以用于反向写数据
        @Override
        public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
        }
    };

    //4.模拟数据更新
    private void updateCharacteristic() {
        if (mBluetoothGattServer == null) {
            return;
        }
        final Handler updateHandler = new Handler();
        updateHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                for (BluetoothDevice d : mRegisteredDevices) {
                    BluetoothGattCharacteristic newCharacteristic = mBluetoothGattServer
                            .getService(TIME_SERVICE)
                            .getCharacteristic(CURRENT_TIME);
                    byte[] data = ("数据更新" + mDateFormat.format(System.currentTimeMillis())).getBytes();
                    newCharacteristic.setValue(data);
                    mBluetoothGattServer.notifyCharacteristicChanged(d, newCharacteristic, false);
                }
                updateHandler.postDelayed(this, 5000);

            }
        }, 5000);//5s更新一次
        outputLog("模拟数据更新启动=====");
    }

    BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                case BluetoothAdapter.ACTION_STATE_CHANGED:
                    int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0);
                    switch (blueState) {
                        case BluetoothAdapter.STATE_TURNING_ON:
                            outputLog("蓝牙正在打开...");
//                            bleEnable = false;
                            break;
                        case BluetoothAdapter.STATE_ON:
                            outputLog("蓝牙已打开");
//                            bleEnable = true;
//                            getBleOpenStatus();
//                            switch_ble.setEnabled(true);
                            createGattServer();//所以以下这两个方法在这里直接运行是错误的，一定要在蓝牙正确开启，并且支持BLE在执行
                            startAdvertising();//我写在这里只是为了展示调用顺序。切记切记！！
                            break;
                        case BluetoothAdapter.STATE_TURNING_OFF:
                            outputLog("蓝牙正在关闭...");
//                            bleEnable = false;
//                            getBleOpenStatus();
                            break;
                        case BluetoothAdapter.STATE_OFF:
                            outputLog("蓝牙已关闭");
//                            bleEnable = false;
//                            switch_ble.setEnabled(true);
                            break;
                    }
                    break;
                case BluetoothAdapter.ACTION_SCAN_MODE_CHANGED:
                    Log.i(TAG, "onReceive: scan mode changed");
//                    getAllowSearchStatus();
                    break;
                case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
                    Log.i(TAG, "onReceive: discovery started");
                    break;
                case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                    Log.i(TAG, "onReceive: discovery finished");
                    break;
                case BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED:
                    Log.i(TAG, "onReceive: connection state changed");
                    break;
                case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    switch (device.getBondState()) {
                        case BluetoothDevice.BOND_NONE:
                            Log.e(getPackageName(), "取消配对");
                            break;
                        case BluetoothDevice.BOND_BONDING:
                            Log.e(getPackageName(), "配对中");
                            break;
                        case BluetoothDevice.BOND_BONDED:
                            Log.e(getPackageName(), "配对成功");
                            break;
                    }
                    break;
            }
        }
    };

    private void outputLog(String msg) {
        if (mMessageAdapter != null) {
            Message message = new Message();
            message.what = 0;
            Bundle bundle = new Bundle();
            bundle.putString("msg", msg);
            message.setData(bundle);
            handler.sendMessage(message);
            Log.d(TAG, "outputLog: " + msg);
        }
    }

    protected void onDestroy() {
        if (mBluetoothLeAdvertiser != null) {
            mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback);
        }
        if (mBluetoothGattServer != null) {
            mBluetoothGattServer.close();
        }
        super.onDestroy();
    }
}
